package net.rjtx.ksrs.util.excel;

import com.opencsv.CSVReader;
import net.rjtx.ksrs.service.SmsExcelToDbService;
import net.rjtx.ksrs.thread.SaveRowCount;
import net.rjtx.ksrs.thread.SaveThread;
import net.rjtx.ksrs.util.AnnotionUtil;
import net.rjtx.ksrs.util.EncodeUtil;
import net.rjtx.ksrs.util.EncodingDetect;
import net.rjtx.ksrs.util.FileUtil;
import net.rjtx.ksrs.util.constants.Constants;
import net.rjtx.ksrs.web.rest.errors.CustomParameterizedException;
import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.web.bind.annotation.GetMapping;

import java.io.*;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.time.Instant;
import java.util.*;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.stream.Collectors;

public class ExcelCsvReader {

    private final Logger log = LoggerFactory.getLogger(this.getClass());

    private SmsExcelToDbService smsExcelToDbService;


    public ExcelCsvReader(SmsExcelToDbService smsExcelToDbService) {
        this.smsExcelToDbService = smsExcelToDbService;
    }

    /**
     * 逐行读取csv文件,读取到一定量的行数(pageSize)后保存到数据库
     *
     * @param fileName
     * @param pageSize
     * @param excelDataType
     * @param readerEncoding
     * @param lastModifiedDatetime
     * @return
     * @throws IOException
     */
    public int[] readAndSave(String fileName, int pageSize, Constants.ExcelDataType excelDataType
            , String readerEncoding, Instant lastModifiedDatetime) throws IOException {

        //第一个数表示读取到的数据总条数,第二个数表示保存成功的总条数
        int[] result = {0, 0};
        //key是开始行号和value结束行号
        Hashtable<Integer, Integer> errLineNo = new Hashtable<>();
        final List<List<String>> page = new ArrayList<>();
        final ArrayList<String> cellList = new ArrayList<>();
        File file = new File(fileName);
        if (!file.exists()) {
            return result;
        }
        try (BufferedReader reader = new BufferedReader(new InputStreamReader(
                new FileInputStream(file), readerEncoding));) {

            Document document = excelDataType.getTargetClass().getAnnotation(Document.class);
            if (document != null) {
                String indexName = document.indexName();
                if (indexName != null) {
                    smsExcelToDbService.createElasticSearchIndex(indexName);
                }
            }

            CSVReader csvReader = new CSVReader(reader, ',');
//            String regex = ",(?=([^\\\"]*\\\"[^\\\"]*\\\")*[^\\\"]*$)";//CSV格式文件为逗号分隔符文件，这里根据逗号切分
            String line = null;
            String[] strings = null;
            for (result[0] = 0; (strings = csvReader.readNext()) != null; result[0] = result[0] + 1) {
//            for (result[0] = 0; (line = reader.readLine()) != null; result[0] = result[0] + 1) {
//                String[] strings = line.split(",");
                cellList.clear();
                //第一行是表头,不读取
                if (result[0] == 0) {
                    continue;
                }
                for (int i = 0; i < strings.length; i++) {
                    String string = strings[i];

                    string = ExcelSupport.reversalChineseCharacter(string);
                    cellList.add(string);
                }
                page.add(cellList.stream().collect(Collectors.toList()));

                if (page.size() > 0 && page.size() % pageSize == 0) {
                    try {
//                     = page.size();
                        int saveRows = this.smsExcelToDbService.saveAll(page, excelDataType, pageSize, lastModifiedDatetime);
                        result[1] += saveRows;
                    } catch (Exception e) {
                        errLineNo.put(result[0], result[0] + pageSize);
                        e.printStackTrace();
                    } finally {
                        page.clear();
                    }
                }
            }

            //相当于 flush , 保存最后不够pageSize数目的数据条数
            if (page.size() > 0 && page.size() < pageSize) {
                try {
                    int saveRows = this.smsExcelToDbService.saveAll(page, excelDataType, pageSize, lastModifiedDatetime);
                    result[1] += saveRows;
                } catch (Exception e) {
                    errLineNo.put(result[0], result[0] + pageSize);
                    e.printStackTrace();
                } finally {
                    page.clear();
                }
            }
            for (Integer startNo : errLineNo.keySet()) {
                log.info("写入数据库失败的行号:" + startNo + " --- " + errLineNo.get(startNo));
            }
        } finally {
            if (file.exists()) {
                boolean delete = file.delete();
                if (!delete) {
                    System.gc();
                    file.delete();
                }
            }
        }
        return result;
    }

    //获取线程池
    private ThreadPoolTaskExecutor getNewThreadPool() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor() {

        };
        //核心线程数，默认为1
        executor.setCorePoolSize(3);
        //最大线程数，默认为Integer.MAX_VALUE
        executor.setMaxPoolSize(15);
        //线程池维护线程所允许的空闲时间，默认为60s
        executor.setKeepAliveSeconds(60);
        //队列最大长度，一般需要设置值>=notifyScheduledMainExecutor.maxNum；默认为Integer.MAX_VALUE
        executor.setQueueCapacity(10);
        //线程池对拒绝任务（无线程可用）的处理策略，目前只支持 AbortPolicy、CallerRunsPolicy；默认为后者
        //AbortPolicy:直接抛出java.util.concurrent.RejectedExecutionException异常
        //CallerRunsPolicy:主线程直接执行该任务，执行完之后尝试添加下一个任务到线程池中，可以有效降低向线程池内添加任务的速度
        //DiscardOldestPolicy:抛弃旧的任务、暂不支持；会导致被丢弃的任务无法再次被执行
        //DiscardPolicy:抛弃当前任务、暂不支持；会导致被丢弃的任务无法再次被执行
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        //调用 executor.shutdown() 后线程池等待任务执行完毕后在注销
        executor.setWaitForTasksToCompleteOnShutdown(true);
        executor.initialize();
        return executor;

    }

    /**
     * 逐行读取csv文件,读取到一定量的行数(pageSize)后保存到数据库
     *
     * @param fileName
     * @param pageSize
     * @param excelDataType
     * @param readerEncoding
     * @param lastModifiedDatetime
     * @return
     * @throws IOException
     */
    public int[] readAndSaveThread(String fileName, int pageSize, Constants.ExcelDataType excelDataType
            , String readerEncoding, Instant lastModifiedDatetime
            , Integer sceneType) throws IOException {

        ThreadPoolTaskExecutor executor = getNewThreadPool();

        //第一个数表示读取到的数据总条数,第二个数表示保存成功的总条数
        int[] result = {0, 0};
        //key是开始行号和value结束行号
        Hashtable<Integer, Integer> errLineNo = new Hashtable<>();
        final List<List<String>> page = new ArrayList<>();
        final ArrayList<String> cellList = new ArrayList<>();
        File file = new File(fileName);
        if (!file.exists()) {
            return result;
        }
        Long lines = 0L;
        try {
            lines = Files.lines(Paths.get(file.getPath()), Charset.forName(readerEncoding)).count();
        } catch (Exception e) {
            try {
                readerEncoding = EncodeUtil.getInstance().getEncode(file.getPath(), readerEncoding);
                lines = Files.lines(Paths.get(file.getPath()), Charset.forName(readerEncoding)).count();
            } catch (Exception e1) {
                e1.printStackTrace();
                try {
                    readerEncoding = new EncodingDetect().detect(file.getPath());
                    lines = Files.lines(Paths.get(file.getPath()), Charset.forName(readerEncoding)).count();
                } catch (Exception e2) {
                    e2.printStackTrace();
                    throw new CustomParameterizedException("无法识别的编码...");
                }
            }
        }
        int threadNumbers = Long.valueOf((lines / pageSize) + (lines % pageSize > 0 ? 1 : 0)).intValue();
        log.info("预期开启的线程总数[{}]", threadNumbers);
        if (threadNumbers == 0) {
            threadNumbers = 10000;
            log.info("计算预期线程总数的时候出错了,修改为预期开启线程数[{}]", threadNumbers);
        }

        int realThreadNumbers = 0;
        //记录开启的线程数
        CountDownLatch doneSignal = new CountDownLatch(threadNumbers);
        try (BufferedReader reader = new BufferedReader(new InputStreamReader(
                new FileInputStream(file), readerEncoding));) {

            Document document = excelDataType.getTargetClass().getAnnotation(Document.class);
            if (document != null) {
                String indexName = document.indexName();
                if (indexName != null) {
                    smsExcelToDbService.createElasticSearchIndex(indexName);
                }
            }

            CSVReader csvReader = new CSVReader(reader, ',');
//            String regex = ",(?=([^\\\"]*\\\"[^\\\"]*\\\")*[^\\\"]*$)";//CSV格式文件为逗号分隔符文件，这里根据逗号切分
            String line = null;
            String[] strings = null;
            for (result[0] = 0; (strings = csvReader.readNext()) != null; result[0] = result[0] + 1) {
//            for (result[0] = 0; (line = reader.readLine()) != null; result[0] = result[0] + 1) {
//                String[] strings = line.split(",");
                cellList.clear();
                //第一行是表头,不读取
                if (result[0] == 0) {
                    continue;
                }
                for (int i = 0; i < strings.length; i++) {
                    String string = strings[i];

                    string = ExcelSupport.reversalChineseCharacter(string);
                    cellList.add(string);
                }
                page.add(cellList.stream().collect(Collectors.toList()));

                if (page.size() > 0 && page.size() % pageSize == 0) {
                    try {
                        log.info("已开启的线程总数[{}],预期总数[{}]", ++realThreadNumbers, threadNumbers);
//                        System.out.println(page);
                        executor.execute(new SaveThread(smsExcelToDbService, page.stream().collect(Collectors.toList()), excelDataType, lastModifiedDatetime, pageSize, sceneType, doneSignal));
                    } catch (Exception e) {
                        errLineNo.put(result[0], result[0] + pageSize);
                        e.printStackTrace();
                    } finally {
                        page.clear();
                    }
                }
            }

            //相当于 flush , 保存最后不够pageSize数目的数据条数
            if (page.size() > 0 && page.size() < pageSize) {
                try {
                    log.info("已开启的线程总数[{}],预期总数[{}]", ++realThreadNumbers, threadNumbers);
                    executor.execute(new SaveThread(smsExcelToDbService, page.stream().collect(Collectors.toList()), excelDataType, lastModifiedDatetime, pageSize, sceneType, doneSignal));
                } catch (Exception e) {
                    errLineNo.put(result[0], result[0] + pageSize);
                    e.printStackTrace();
                } finally {
                    page.clear();
                }
            }


            //减去 doneSignal 中多了的预期线程数
            for (int numbers = realThreadNumbers - threadNumbers; numbers > 0; numbers--) {
                doneSignal.countDown();
            }
            //确认所有线程任务完成，开始执行主线程的操作
            try {
                log.info("预期开启线程总数[{}],实际开启的线程总数[{}],当前还剩需要执行的线程总数[{}]", threadNumbers, realThreadNumbers, doneSignal.getCount());
                doneSignal.await();
                executor.shutdown();//
                result[0] = SaveRowCount.getCountObject(lastModifiedDatetime.toEpochMilli()).getRowCount();
                result[1] = SaveRowCount.getCountObject(lastModifiedDatetime.toEpochMilli()).getSaveSuccessRowCount();
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }

            for (Integer startNo : errLineNo.keySet()) {
                log.info("写入数据库失败的行号:" + startNo + " --- " + errLineNo.get(startNo));
            }
        } finally {
            //这里需要做个判断，所有做 save 工作线程全部执行完。
            SaveRowCount k = SaveRowCount.getCountObject(lastModifiedDatetime.toEpochMilli());
            //Map<String,Integer> resultMap = k.getMap();
            log.info("读取到的总行数：" + result[0]);
            log.info("线程读取到的总行数：" + k.getSaveSuccessRowCount());
            log.info("保存的行数：" + k.getSaveSuccessRowCount());
            SaveRowCount.getCountObject(lastModifiedDatetime.toEpochMilli()).closeObject();
            if (file.exists()) {
                boolean delete = file.delete();
                if (!delete) {
                    System.gc();
                    file.delete();
                }
            }
        }
        return result;
    }

    /**
     * 逐行读取csv文件,读取到一定量的行数(pageSize)后保存到数据库
     *
     * @param fileForParseFullPath
     * @param readerEncoding
     * @return
     * @throws IOException
     */
//    public int[] readAll(String tempDirPath, String fileForParseFullPath, String readerEncoding) throws IOException {
//
//        ExcelSupport excelSupport = null;
//        try {
//            excelSupport = new ExcelSupport(tempDirPath, fileForParseFullPath, 1, "GBK");
//        } catch (Exception e) {
//            e.printStackTrace();
//        }
//
//
//        //第一个数表示读取到的数据总条数,第二个数表示保存成功的总条数
//        int[] result = {0, 0};
//        //key是开始行号和value结束行号
//        Hashtable<Integer, Integer> errLineNo = new Hashtable<>();
//        final List<List<String>> page = new ArrayList<>();
//        HashMap<String, Object> cgi_rdn_map = new HashMap<>();
//        final ArrayList<String> cellList = new ArrayList<>();
//        File file = new File(fileForParseFullPath);
//        if (!file.exists()) {
//            return result;
//        }
//        try (BufferedReader reader = new BufferedReader(new InputStreamReader(
//                new FileInputStream(file), readerEncoding));
//             BufferedReader readerRdn = new BufferedReader(new InputStreamReader(
//                     new FileInputStream(file), readerEncoding));) {
//
//            //读取rdn
//            Integer cgiIndexRdn = AnnotionUtil.getInstance().excelColumnNameToIndex("E");
//            Integer rdnIndexRdn = AnnotionUtil.getInstance().excelColumnNameToIndex("E");
//            CSVReader csvReaderRdn = new CSVReader(readerRdn, ',');
//            String[] rdnstrs = null;
//            for (result[0] = 0; (rdnstrs = csvReaderRdn.readNext()) != null; result[0] = result[0] + 1) {
//                cellList.clear();
//                //第一行是表头,不读取
//                if (result[0] == 0) {
//                    continue;
//                }
//                for (int i = 0; i < rdnstrs.length; i++) {
//                    String string = rdnstrs[i];
//                    string = ExcelSupport.reversalChineseCharacter(string);
//                    cellList.add(string);
//                }
//                page.add(cellList.stream().collect(Collectors.toList()));
//                String cgi = cellList.get(cgiIndexRdn).trim();
//                String rdn = cellList.get(rdnIndexRdn).trim();
//                cgi_rdn_map.put(cgi, rdn);
//            }
//
//
//            Integer cgiIndex = AnnotionUtil.getInstance().excelColumnNameToIndex("E");
//            CSVReader csvReader = new CSVReader(reader, ',');
//            String[] strings = null;
//            for (result[0] = 0; (strings = csvReader.readNext()) != null; result[0] = result[0] + 1) {
//                cellList.clear();
//                //第一行是表头,不读取
//                if (result[0] == 0) {
//                    continue;
//                }
//                for (int i = 0; i < strings.length; i++) {
//                    String string = strings[i];
//                    string = ExcelSupport.reversalChineseCharacter(string);
//                    cellList.add(string);
//                }
//                page.add(cellList.stream().collect(Collectors.toList()));
//
//                String cgi = cellList.get(cgiIndex).trim();
//                if (cgi_rdn_map.get(cgi) == null) {
//
//                }
//            }
//
//
//            for (Integer startNo : errLineNo.keySet()) {
//                log.info("写入数据库失败的行号:" + startNo + " --- " + errLineNo.get(startNo));
//            }
//        } finally {
//            if (file.exists()) {
//                boolean delete = file.delete();
//                if (!delete) {
//                    System.gc();
//                    file.delete();
//                }
//            }
//        }
//        return result;
//    }

    /**
     * 逐行读取csv文件,读取到一定量的行数(pageSize)后保存到数据库
     *
     * @param fileName
     * @param pageSize
     * @param excelDataType
     * @param readerEncoding
     * @param lastModifiedDatetime
     * @return
     * @throws IOException
     */
    public int[] readAndSaveDriverTestData(String fileName, int pageSize, Constants.ExcelDataType excelDataType
            , String readerEncoding, Instant lastModifiedDatetime
            , Integer sceneType) throws IOException {

        //第一个数表示读取到的数据总条数,第二个数表示保存成功的总条数
        int[] result = {0, 0};
        if (!excelDataType.isSmsDriverTest() || sceneType == null) {
            return result;
        }

        //key是开始行号和value结束行号
        Hashtable<Integer, Integer> errLineNo = new Hashtable<>();
        final List<List<String>> page = new ArrayList<>();
        final ArrayList<String> cellList = new ArrayList<>();
        File file = new File(fileName);
        if (!file.exists()) {
            return result;
        }

//        Long lines = Files.lines(Paths.get(file.getPath()), Charset.forName(readerEncoding)).count();
        Long lines = 0L;
        try {
            lines = Files.lines(Paths.get(file.getPath()), Charset.forName(readerEncoding)).count();
        } catch (Exception e) {
            try {
//                EncodingDetect.getJavaEncode(filePath);

                readerEncoding = EncodeUtil.getInstance().getEncode(file.getPath(), readerEncoding);
                lines = Files.lines(Paths.get(file.getPath()), Charset.forName(readerEncoding)).count();
            } catch (Exception e1) {
                e1.printStackTrace();
            }
        }
        int threadNumbers = Long.valueOf((lines / pageSize) + (lines % pageSize > 0 ? 1 : 0)).intValue();

        //线程池
        ThreadPoolTaskExecutor executor = getNewThreadPool();

        CountDownLatch doneSignal = new CountDownLatch(threadNumbers);
        int realThreadNumbers = 0;
        try (BufferedReader reader = new BufferedReader(new InputStreamReader(
                new FileInputStream(file), readerEncoding));) {

            Document document = excelDataType.getTargetClass().getAnnotation(Document.class);
            if (document != null) {
                String indexName = document.indexName();
                if (indexName != null) {
                    smsExcelToDbService.createElasticSearchIndex(indexName);
                }
            }

            String regex = ",(?=([^\\\"]*\\\"[^\\\"]*\\\")*[^\\\"]*$)";//特殊分隔符文件，这里根据 readerSeparator 的值切分
            String line = null;
//            CSVReader csvReader = new CSVReader(reader, ',');
//            for (result[0] = 0; (strings = csvReader.readNext()) != null; result[0] = result[0] + 1) {
            for (result[0] = 0; (line = reader.readLine()) != null; result[0] = result[0] + 1) {
                String[] strings = line.split(regex);
                cellList.clear();
                //第一行是表头,不读取
                if (result[0] == 0) {
                    continue;
                }
                for (int i = 0; i < strings.length; i++) {
                    String string = strings[i];
                    string = ExcelSupport.reversalChineseCharacter(string);
                    cellList.add(string);
                }
                page.add(cellList.stream().collect(Collectors.toList()));

                if (page.size() > 0 && page.size() % pageSize == 0) {
                    try {
                        log.info("已开启的线程总数[{}],预期总数[{}]", ++realThreadNumbers, threadNumbers);
                        executor.execute(new SaveThread(smsExcelToDbService, page.stream().collect(Collectors.toList()), excelDataType, lastModifiedDatetime, pageSize, sceneType, doneSignal));
//                        new SaveThread(smsExcelToDbService, page.stream().collect(Collectors.toList()), excelDataType, lastModifiedDatetime, pageSize, sceneType, doneSignal).start();
                    } catch (Exception e) {
                        errLineNo.put(result[0], result[0] + pageSize);
                        e.printStackTrace();
                    } finally {
                        page.clear();
                    }
                }
            }

            //相当于 flush , 保存最后不够pageSize数目的数据条数
            if (page.size() > 0 && page.size() < pageSize) {
                try {
                    log.info("已开启的线程总数[{}],预期总数[{}]", ++realThreadNumbers, threadNumbers);
                    executor.execute(new SaveThread(smsExcelToDbService, page.stream().collect(Collectors.toList()), excelDataType, lastModifiedDatetime, pageSize, sceneType, doneSignal));
//                    new SaveThread(smsExcelToDbService, page.stream().collect(Collectors.toList()), excelDataType, lastModifiedDatetime, pageSize, sceneType, doneSignal).start();
                } catch (Exception e) {
                    errLineNo.put(result[0], result[0] + pageSize);
                    e.printStackTrace();
                } finally {
                    page.clear();
                }
            }

            //减去 doneSignal 中多了的预期线程数
            for (int numbers = realThreadNumbers - threadNumbers; numbers > 0; numbers--) {
                doneSignal.countDown();
            }
            //确认所有线程任务完成，开始执行主线程的操作
            try {
                log.info("预期开启线程总数[{}],实际启的线程总数[{}],当前仍在执行的线程总数[{}]", threadNumbers, realThreadNumbers, doneSignal.getCount());
                doneSignal.await();
                executor.shutdown();
                result[0] = SaveRowCount.getCountObject(lastModifiedDatetime.toEpochMilli()).getRowCount();
                result[1] = SaveRowCount.getCountObject(lastModifiedDatetime.toEpochMilli()).getSaveSuccessRowCount();
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }

            for (Integer startNo : errLineNo.keySet()) {
                log.info("写入数据库失败的行号:" + startNo + " --- " + errLineNo.get(startNo));
            }
        } finally {
            //这里需要做个判断，所有做 save 工作线程全部执行完。
            SaveRowCount k = SaveRowCount.getCountObject(lastModifiedDatetime.toEpochMilli());
            //Map<String,Integer> resultMap = k.getMap();
            log.info("读取到的总行数：" + result[0]);
            log.info("线程读取到的总行数：" + k.getSaveSuccessRowCount());
            log.info("保存的行数：" + k.getSaveSuccessRowCount());
            SaveRowCount.getCountObject(lastModifiedDatetime.toEpochMilli()).closeObject();
            if (file.exists()) {
                boolean delete = file.delete();
                if (!delete) {
                    System.gc();
                    file.delete();
                }
            }
        }
        return result;
    }

    private ExcelSupport excelSupport;

    /**
     * 转换为可导入的csv
     *
     * @param fileForParseFullPath
     * @param readerEncoding
     * @param readerSeparator
     * @return
     * @throws Exception
     */
    public String toCsv(String tempDirPath, String fileForParseFullPath, String readerEncoding, String readerSeparator) throws Exception {
        //key是开始行号和value结束行号
        File file = new File(fileForParseFullPath);
        if (!file.exists()) {
            return null;
        }
        excelSupport = new ExcelSupport(tempDirPath, fileForParseFullPath, 0, readerEncoding);
        try (BufferedReader reader = new BufferedReader(new InputStreamReader(
                new FileInputStream(file), readerEncoding));) {
//            CSVReader csvReader = new CSVReader(reader, readerSeparator);
            String regex = readerSeparator + "(?=([^\\\"]*\\\"[^\\\"]*\\\")*[^\\\"]*$)";//特殊分隔符文件，这里根据 readerSeparator 的值切分
            String line = null;
//            while ((strings = csvReader.readNext()) != null) {
            boolean isFirstRow = true;
            int cellCount = 0;
            while ((line = reader.readLine()) != null) {
                String[] strings = line.split(regex);
                //记录首行标题的单元格数
                if (isFirstRow) {
                    isFirstRow = false;
                    cellCount = strings.length;
                }
                for (int i = 0; i < strings.length; i++) {
                    String string = strings[i];
                    string = ExcelSupport.reversalChineseCharacter(string);
                    excelSupport.printStreamWriteToCsv(string);
//                excelSupport.printStreamWriteToCsv(",");
                }
                //末尾单元格填充
                for (int i = 0; i < cellCount - strings.length; i++) {
                    excelSupport.printStreamWriteToCsv("");
                }
                excelSupport.printStreamWriteNextLineToCsv();
            }
            excelSupport.close();
            return excelSupport.getCsvName();
        } finally {

        }
    }

}
